#include "socket.h"
#include "iomanager.h"
#include "fd_manager.h"
#include "log.h"
#include "macro.h"
#include "hook.h"
#include <limits.h>

namespace johnsonli
{
    static johnsonli::Logger::ptr g_logger = LOG_NAME("system");

    Socket::ptr Socket::CreateTCP(johnsonli::Address::ptr address) 
    {
        Socket::ptr sock(new Socket(address->getFamily(), TCP, 0));
        return sock;
    }

    Socket::ptr Socket::CreateUDP(johnsonli::Address::ptr address) 
    {
        Socket::ptr sock(new Socket(address->getFamily(), UDP, 0));
        sock->newSock();
        sock->m_isConnected = true;
        return sock;
    }

    Socket::ptr Socket::CreateTCPSocket() 
    {
        Socket::ptr sock(new Socket(IPv4, TCP, 0));
        return sock;
    }

    Socket::ptr Socket::CreateUDPSocket() 
    {
        Socket::ptr sock(new Socket(IPv4, UDP, 0));
        sock->newSock();
        sock->m_isConnected = true;
        return sock;
    }

    Socket::ptr Socket::CreateTCPSocket6() 
    {
        Socket::ptr sock(new Socket(IPv6, TCP, 0));
        return sock;
    }

    Socket::ptr Socket::CreateUDPSocket6() 
    {
        Socket::ptr sock(new Socket(IPv6, UDP, 0));
        sock->newSock();
        sock->m_isConnected = true;
        return sock;
    }



    Socket::Socket(int family, int type, int protocol)
        :m_sockfd(-1)
        ,m_family(family)
        ,m_type(type)
        ,m_protocol(protocol)
        ,m_isConnected(false) {
    }

    Socket::~Socket() {
        close();
    }

    int64_t Socket::getSendTimeout() {
        FdCtx::ptr ctx = FdMgr::GetInstance()->get(m_sockfd);
        if(ctx) {
            return ctx->getTimeout(SO_SNDTIMEO);
        }
        return -1;
    }

    void Socket::setSendTimeout(int64_t v) {
        struct timeval tv{int(v / 1000), int(v % 1000 * 1000)};
        setOption(SOL_SOCKET, SO_SNDTIMEO, tv);
        // FdMgr::GetInstance()->get(m_sockfd)->setTimeout(SO_SNDTIMEO, (uint64_t)v);  //在setsockopt的hook做了
    }

    int64_t Socket::getRecvTimeout() {
        FdCtx::ptr ctx = FdMgr::GetInstance()->get(m_sockfd);
        if(ctx) {
            return ctx->getTimeout(SO_RCVTIMEO);
        }
        return -1;
    }

    void Socket::setRecvTimeout(int64_t v) {
        struct timeval tv{int(v / 1000), int(v % 1000 * 1000)};
        setOption(SOL_SOCKET, SO_RCVTIMEO, tv);
        // FdMgr::GetInstance()->get(m_sockfd)->setTimeout(SO_RCVTIMEO, (uint64_t)v);  //在setsockopt的hook做了
    }

    bool Socket::getOption(int level, int option, void* result, socklen_t* len) {
        int rt = getsockopt(m_sockfd, level, option, result, (socklen_t*)len);
        if(rt) {
            LOG_DEBUG(g_logger) << "getOption sock=" << m_sockfd
                << " level=" << level << " option=" << option
                << " errno=" << errno << " errstr=" << strerror(errno);
            return false;
        }
        return true;
    }
    
    bool Socket::setOption(int level, int option, const void* result, socklen_t len) {
        if(setsockopt(m_sockfd, level, option, result, (socklen_t)len)) {
            LOG_DEBUG(g_logger) << "setOption sock=" << m_sockfd
                << " level=" << level << " option=" << option
                << " errno=" << errno << " errstr=" << strerror(errno);
            return false;
        }
        return true;
    }

    Socket::ptr Socket::accept() 
    {
        //创建一个socket
        Socket::ptr connect_socket(new Socket(m_family, m_type, m_protocol));
        //与客户端建立连接，返回连接socket
        int cfd = ::accept(m_sockfd, nullptr, nullptr);
        if(cfd == -1) 
        {
            LOG_ERROR(g_logger) << "accept(" << m_sockfd << ") errno="
                << errno << " errstr=" << strerror(errno);
            return nullptr;
        }

        //初始化连接socket,并返回
        if(connect_socket->init(cfd))
        {
            return connect_socket;
        }

        return nullptr;
    }

    bool Socket::init(int sockfd) 
    {
        //传入一个socket fd，初始化
        FdCtx::ptr ctx = FdMgr::GetInstance()->get(sockfd);
        if(ctx && ctx->isSocket() && !ctx->isClose()) 
        {
            m_sockfd = sockfd;
            m_isConnected = true;
            initSock();
            getLocalAddress();
            getRemoteAddress();
            return true;
        }
        return false;
    }

    bool Socket::bind(const Address::ptr addr) 
    {
        //无效
        if(!isValid()) 
        {
            //创建一个
            newSock();
            if(!isValid()) 
            {
                return false;
            }
        }

        //协议簇不同
        if(addr->getFamily() != m_family)
        {
            LOG_ERROR(g_logger) << "bind sock.family("
                << m_family << ") addr.family(" << addr->getFamily()
                << ") not equal, addr=" << addr->toString();
            return false;
        }

        if(::bind(m_sockfd, addr->getAddr(), addr->getAddrLen())) 
        {
            LOG_ERROR(g_logger) << "bind error errrno=" << errno
                << " errstr=" << strerror(errno);
            return false;
        }

        getLocalAddress();
        return true;
    }

    bool Socket::reconnect(uint64_t timeout_ms) 
    {
        if(!m_remoteAddress) {
            LOG_ERROR(g_logger) << "reconnect m_remoteAddress is null";
            return false;
        }
        m_localAddress.reset();
        return connect(m_remoteAddress, timeout_ms);
    }

    bool Socket::connect(const Address::ptr addr, uint64_t timeout_ms) 
    {
        m_remoteAddress = addr;
        //无效
        if(!isValid()) 
        {
            newSock();
            if(!isValid()) 
            {
                return false;
            }
        }

        //协议簇不同
        if(addr->getFamily() != m_family)
        {
            LOG_ERROR(g_logger) << "bind sock.family("
                << m_family << ") addr.family(" << addr->getFamily()
                << ") not equal, addr=" << addr->toString();
            return false;
        }
        
        //没有超时
        if(timeout_ms == (uint64_t)-1) 
        {
            if(::connect(m_sockfd, addr->getAddr(), addr->getAddrLen())) 
            {
                LOG_ERROR(g_logger) << "sock=" << m_sockfd << " connect(" << addr->toString()
                    << ") error errno=" << errno << " errstr=" << strerror(errno);
                close();
                return false;
            }
        } 
        else 
        {
            //设置了超时
            if(::connect_with_timeout(m_sockfd, addr->getAddr(), addr->getAddrLen(), timeout_ms))
            {
                LOG_ERROR(g_logger) << "sock=" << m_sockfd << " connect(" << addr->toString()
                    << ") timeout=" << timeout_ms << " error errno="
                    << errno << " errstr=" << strerror(errno);
                close();
                return false;
            }
        }
        m_isConnected = true;
        getRemoteAddress();
        getLocalAddress();
        return true;

    }

    bool Socket::listen(int backlog) 
    {
        //无效
        if(!isValid()) 
        {
            newSock();
            if(!isValid()) 
            {
                return false;
            }
        }
        
        if(::listen(m_sockfd, backlog)) 
        {
            LOG_ERROR(g_logger) << "listen error errno=" << errno
                << " errstr=" << strerror(errno);
            return false;
        }

        return true;
    }

    bool Socket::close() 
    {
        //该socket没有建立连接，或无效
        if(!m_isConnected && !isValid()) 
        {
            return true;
        }

        //关闭连接
        m_isConnected = false;

        //sockfd有效，关闭fd
        if(isValid()) 
        {
            ::close(m_sockfd);
            m_sockfd = -1;
        }
        return false;
    }

    int Socket::send(const void* buffer, size_t length, int flags) 
    {
         if(isConnected()) 
         {
            return ::send(m_sockfd, buffer, length, flags);
        }
        return -1;
    }

    int Socket::send(const iovec* buffers, size_t length, int flags) 
    {
        if(isConnected()) 
        {
            msghdr msg;
            memset(&msg, 0, sizeof(msg));
            msg.msg_iov = (iovec*)buffers;
            msg.msg_iovlen = length;
            return ::sendmsg(m_sockfd, &msg, flags);
        }
        return -1;
    }

    int Socket::sendTo(const void* buffer, size_t length, const Address::ptr to, int flags) 
    {
        if(isConnected()) 
        {
            return ::sendto(m_sockfd, buffer, length, flags, to->getAddr(), to->getAddrLen());
        }
        return -1;
    }

    int Socket::sendTo(const iovec* buffers, size_t length, const Address::ptr to, int flags)
    {
        if(isConnected()) 
        {
            msghdr msg;
            memset(&msg, 0, sizeof(msg));
            msg.msg_iov = (iovec*)buffers;
            msg.msg_iovlen = length;
            msg.msg_name = to->getAddr();
            msg.msg_namelen = to->getAddrLen();
            return ::sendmsg(m_sockfd, &msg, flags);
        }
        return -1;
    }

    int Socket::recv(void* buffer, size_t length, int flags) 
    {
        if(isConnected()) 
        {
           //LOG_INFO(g_logger) << "isConnected()";
            return ::recv(m_sockfd, buffer, length, flags);
        }

        //LOG_INFO(g_logger) << "else isConnected()";
        return -1;
    }

    int Socket::recv(iovec* buffers, size_t length, int flags) 
    {
        if(isConnected()) 
        {
            msghdr msg;
            memset(&msg, 0, sizeof(msg));
            msg.msg_iov = (iovec*)buffers;
            msg.msg_iovlen = length;
            return ::recvmsg(m_sockfd, &msg, flags);
        }
        return -1;
    }

    int Socket::recvFrom(void* buffer, size_t length, Address::ptr from, int flags) 
    {
        if(isConnected()) 
        {
            socklen_t len = from->getAddrLen();
            return ::recvfrom(m_sockfd, buffer, length, flags, from->getAddr(), &len);
        }
        return -1;
    }

    int Socket::recvFrom(iovec* buffers, size_t length, Address::ptr from, int flags) 
    {
        if(isConnected()) 
        {
            msghdr msg;
            memset(&msg, 0, sizeof(msg));
            msg.msg_iov = (iovec*)buffers;
            msg.msg_iovlen = length;
            msg.msg_name = from->getAddr();
            msg.msg_namelen = from->getAddrLen();
            return ::recvmsg(m_sockfd, &msg, flags);
        }
        return -1;
    }

    Address::ptr Socket::getRemoteAddress() 
    {
        if(m_remoteAddress) 
            {
            return m_remoteAddress;
        }

        Address::ptr result;
        switch(m_family) 
        {
            case AF_INET:
                result.reset(new IPv4Address());
                break;
            case AF_INET6:
                result.reset(new IPv6Address());
                break;
            default:
                break;
        }
        socklen_t addrlen = result->getAddrLen();
        //获取对端ip地址信息
        if(getpeername(m_sockfd, result->getAddr(), &addrlen)) 
        {
            LOG_ERROR(g_logger) << "getpeername error sock=" << m_sockfd
               << " errno=" << errno << " errstr=" << strerror(errno);
            return nullptr;
        }

        m_remoteAddress = result;
        return m_remoteAddress;
    }

    Address::ptr Socket::getLocalAddress() 
    {
        if(m_localAddress) 
        {
            return m_localAddress;
        }

        Address::ptr result;
        switch(m_family) 
        {
            case AF_INET:
                result.reset(new IPv4Address());
                break;
            case AF_INET6:
                result.reset(new IPv6Address());
                break;
            default:
                break;
        }
        socklen_t addrlen = result->getAddrLen();
        //获取当前sockfd ip地址
        if(getsockname(m_sockfd, result->getAddr(), &addrlen)) 
        {
            LOG_ERROR(g_logger) << "getpeername error sock=" << m_sockfd
               << " errno=" << errno << " errstr=" << strerror(errno);
            return nullptr;
        }

        m_localAddress = result;
        return m_localAddress;
    }

    bool Socket::isValid() const 
    {
        return m_sockfd != -1;
    }

    int Socket::getError() 
    {
        //或如属性失败，就出错
        int error = 0;
        socklen_t len = sizeof(error);
        if(!getOption(SOL_SOCKET, SO_ERROR, &error, &len)) 
        {
            error = errno;
        }
        return error;
    }

    std::ostream& Socket::dump(std::ostream& os) const 
    {
        os << "[Socket sock=" << m_sockfd
        << " is_connected=" << m_isConnected
        << " family=" << m_family
        << " type=" << m_type
        << " protocol=" << m_protocol;
        if(m_localAddress) 
        {
            os << " local_address=" << m_localAddress->toString();
        }
        if(m_remoteAddress) 
        {
            os << " remote_address=" << m_remoteAddress->toString();
        }
        os << "]";
        return os;
    }

    std::string Socket::toString() const 
    {
        std::stringstream ss;
        dump(ss);
        return ss.str();
    }

    bool Socket::cancelRead() 
    {
        return IOManager::GetThis()->cancelEvent(m_sockfd, johnsonli::IOManager::READ);
    }

    bool Socket::cancelWrite() 
    {
        return IOManager::GetThis()->cancelEvent(m_sockfd, johnsonli::IOManager::WRITE);
    }

    bool Socket::cancelAccept() 
    {
        return IOManager::GetThis()->cancelEvent(m_sockfd, johnsonli::IOManager::READ);
    }

    bool Socket::cancelAll() 
    {
        return IOManager::GetThis()->cancelAll(m_sockfd);
    }

    void Socket::initSock() 
    {
        int val = 1;
        setOption(SOL_SOCKET, SO_REUSEADDR, val);
        if(m_type == TCP) {
            setOption(IPPROTO_TCP, TCP_NODELAY, val);
        }
    }

    void Socket::newSock() 
    {
        m_sockfd = socket(m_family, m_type, m_protocol);
        if(m_sockfd != -1)
        {
            //创建socket成功，设置socket属性
            initSock();
        }
        else
        {
            LOG_ERROR(g_logger) << "socket(" << m_family
                << ", " << m_type << ", " << m_protocol << ") errno="
                << errno << " errstr=" << strerror(errno);
        }
    }

    std::ostream& operator<<(std::ostream& os, const Socket& sock) {
        return sock.dump(os);
    }

}